package com.gujiangbo.chapter16;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

/**
 * @Author: gujiangbo.co
 * @Contact: 646832012@qq.com
 * @Date: 2022/4/12 22:06
 * @Version: 1.0
 * @Description: #本地缓存实现
 */
public class JavaWeakHashMap {
    public static void main(String[] args) {

        LinkedHashMapCache cache = new LinkedHashMapCache.Builder<String, Integer>(10).build();
        IntStream.range(0, 6).forEach(v -> cache.put(v + "", v));
        cache.getValuesAll().forEach(System.out::println);

    }
}

/**
 * 本地缓存实现1
 *
 * @param <K>
 * @param <V>
 */
class ConcurrentCache<K, V> {

    private final int size;

    private final Map<K, V> eden;

    private final Map<K, V> longterm;

    public ConcurrentCache(int size) {
        this.size = size;
        this.eden = new ConcurrentHashMap<>(size);
        this.longterm = new WeakHashMap<>(size);
    }

    public V get(K k) {
        V v = this.eden.get(k);
        if (Objects.isNull(v)) {
            synchronized (longterm) {
                v = this.longterm.get(k);
            }
            if (Objects.nonNull(v)) {
                this.eden.put(k, v);
            }
        }
        return v;
    }

    public void put(K k, V v) {
        if (this.eden.size() >= size) {
            synchronized (longterm) {
                this.longterm.putAll(this.eden);
            }
            this.eden.clear();
        }
        this.eden.put(k, v);
    }
}

/**
 * 本地缓存实现2
 *
 * @param <K>
 * @param <V>
 */
class WeakHashMapCache<K, V> {
    private final int size;
    private final Map<K, V> eden;
    private final Map<K, V> longterm;

    private WeakHashMapCache(Builder<K, V> builder) {
        this.size = builder.size;
        this.eden = builder.eden;
        this.longterm = builder.longterm;
    }

    public V get(K k) {
        V v = this.eden.get(k);
        if (Objects.isNull(v)) {
            v = this.longterm.get(k);
            if (Objects.nonNull(v)) {
                this.eden.put(k, v);
            }
        }
        return v;
    }

    public void put(K k, V v) {
        if (this.eden.size() >= size) {
            this.longterm.putAll(this.eden);
            this.eden.clear();
        }
        this.eden.put(k, v);
    }

    static class Builder<K, V> {
        private volatile int size;

        private volatile Map<K, V> eden;

        private volatile Map<K, V> longterm;

        public Builder(int size) {
            this.size = rangeCheck(size, Integer.MAX_VALUE, "缓存容器初始化容量异常");
            this.eden = new ConcurrentHashMap<>(size);
            this.longterm = new WeakHashMap<>(size);
        }

        private static int rangeCheck(int val, int i, String arg) {
            if (val < 0 || val > i) {
                throw new IllegalArgumentException(arg + ":" + val);
            }
            return val;
        }

        public WeakHashMapCache build() {
            return new WeakHashMapCache(this);
        }
    }


}


/**
 * LinkedHashMap 实现本地缓存3
 *
 * @param <K>
 * @param <V>
 */
class LinkedHashMapCache<K, V> {
    private final Map<K, V> eden;

    public LinkedHashMapCache(Builder builder) {
        this.eden = builder.eden;
    }

    public static class Builder<K, V> {
        private volatile Map<K, V> eden;
        private int size;

        public Builder(int size) {
            this.size = rangeCheck(size, Integer.MAX_VALUE, "缓存容器初始化容量异常");
            this.eden = new LinkedHashMap<K, V>(size, 0.75f, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                    return size() >= size;
                }
            };
        }

        private int rangeCheck(int val, int i, String arg) {
            if (val < 0 || val > i) {
                throw new IllegalArgumentException(arg + ":" + val);
            }
            return val;
        }

        public LinkedHashMapCache build() {
            return new LinkedHashMapCache(this);
        }
    }

    public V get(K k) {
        return eden.get(k);
    }

    public void put(K k, V v) {
        this.eden.put(k, v);
    }

    public List<V> getValuesAll() {
        if (eden.size() == 0) {
            return null;
        }
        List<V> values = new ArrayList<>();
        eden.entrySet().stream().forEach(v -> values.add(v.getValue()));
        return values;

    }


}